package com.admgt.resource.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Calendar;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import com.admgt.resource.dao.LoadingInfoDao;
import com.admgt.resource.entity.LoadingInfo;

/**
 * Handle the logics in ResourceFrame
 * @author James
 *
 */
public class ResourceServiceImpl implements ResourceService {
	
	private LoadingInfoDao loadingInfoDao;
	private FTPClient ftpClient;

	public void setLoadingInfoDao(LoadingInfoDao loadingInfoDao) {
		this.loadingInfoDao = loadingInfoDao;
	}
	
	public LoadingInfoDao getLoadingInfoDao() {
		return loadingInfoDao;
	}

	public FTPClient getFtpClient() {
		return ftpClient;
	}


	public void setFtpClient(FTPClient ftpClient) {
		this.ftpClient = ftpClient;
	}


	/**
	 * connect to FTP server and return the connected FTPClient instance, 
	 * otherwise throw out the exception
	 */
	@Override
	public FTPClient connectFtpServer(String hostname,
			int port,
			String username,
			String password) throws FtpUsernameOrPwdException {
		ftpClient = new FTPClient();
		try {
			ftpClient.addProtocolCommandListener(new PrintCommandListener(
					new PrintWriter(System.out)));
			ftpClient.setControlEncoding("UTF-8");
			FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
			conf.setServerLanguageCode("zh");
			
			connect(hostname, port, username, password);
			if(!ftpClient.isConnected()){
				throw new FtpUsernameOrPwdException("FTP服务器连接失败！") ;
			}
		} catch (Exception e) {
			throw new FtpUsernameOrPwdException(e) ;
		}
		return ftpClient;
	}

	@Override
	public boolean initFileType() {
		// TODO Auto-generated method stub
		return false;
	}





	@Override
	public boolean cancelAll() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean back() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean filterByType() {
		// TODO Auto-generated method stub
		return false;
	}

	

	@Override
	public boolean downloadAll() {
		// TODO Auto-generated method stub
		return false;
	}

	
	@Override
	public boolean loadingProgress() {
		// TODO Auto-generated method stub
		return false;
	}




	@Override
	public boolean updateProgressData() {
		// TODO Auto-generated method stub
		return false;
	}
	
	
	enum UploadStatus {
		REMOTE_FILE_NOT_UNIQUE, CREATE_DIRECTORY_FAIL, CREATE_DIRECTORY_SUCCESS, FILE_EXIST, REMOTE_EQUAL_LOCAL, REMOTE_BIGGER_LOCAL, UPLOAD_FROM_BREAK_SUCCESS, UPLOAD_FROM_BREAK_FAILED, DELETE_REMOTE_FAILED, UPLOAD_NEW_FILES_SUCCESS, UPLOAD_NEW_FILES_FAILED;
	}

	public boolean connect(String hostname, int port, String username,
			String password) throws IOException {
		ftpClient.connect(hostname, port);
		if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
			if (ftpClient.login(username, password)) {
				return true;
			}
		}
		disconnect();
		return false;
	}

	public void disconnect() throws IOException {
		if (ftpClient.isConnected()) {
			ftpClient.disconnect();
		}
	}

	/**
	 * download the file from Ftp server to local path.
	 * 
	 * @param ftpServerFileName
	 * @param localPath
	 * @return
	 * @throws IOException
	 */
	public boolean download(String ftpServerFileName, String localPath)
			throws Exception {
		ftpClient.enterLocalPassiveMode();// transfer by passive mode
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// transfer date by byte
													// code
		boolean result;
		// get remote file size
		try {
			FTPFile[] files = ftpClient.listFiles(ftpServerFileName);
			if (files.length != 1) {
				System.out.println("remote file is not unique!");
				return false;
			}
			long remoteSize = files[0].getSize();
			// start to down load files
			File localFile = new File(localPath);
			
			LoadingInfo loadingInfo = new LoadingInfo();
			loadingInfo.setLoadType("up");
			loadingInfo.setFileName(ftpServerFileName);
			System.out.println("ftpServerFileName"+ftpServerFileName);
			loadingInfo.setFileSize(""+((remoteSize>1024)?
					(remoteSize/1024+"."+(remoteSize%1024)/1024*100+"kb"):
						remoteSize));
			loadingInfo.setHostName(ftpClient.getPassiveHost());
			System.out.println("ftpClient.getPassiveHost()="+ftpClient.getPassiveHost());
			
			loadingInfo.setHostName("127.0.0.1");
			System.out.println("ftpClient.getPassiveHost()="+ftpClient.getPassiveHost());
			
			loadingInfo.setLoadStatus("Start");
			loadingInfo.setLocalPath(localPath);
			System.out.println("localPath"+localPath);
			loadingInfo.setServerPath("/");
			loadingInfo.setLoadProgress(0);
			loadingInfo.setUpdatedTime(Calendar.getInstance().getTime());
			loadingInfoDao.addLoadingInfo(loadingInfo);
			
			if (localFile.exists()) {
				result = retryBrokenDownloads(localFile, ftpServerFileName,
						remoteSize);
			} else {
				result = downloadNewFile(localFile, ftpServerFileName);

			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return result;
	}


	@Override
	public Object[] addLoadingInfo(String ftpServerFileName, String localPath)  throws Exception{
		
		System.out.println("in add loading info");
		Object[] rowData=null;
		try {
			FTPFile[] files = ftpClient.listFiles(ftpServerFileName);
			System.out.println("files="+files);
			if (files.length != 1) {
				System.out.println("remote file is not unique!"+files.length);
				//return null;
			}
			long remoteSize = files[0].getSize();
			
			LoadingInfo loadingInfo = new LoadingInfo();
			loadingInfo.setLoadType("up");
			loadingInfo.setFileName(ftpServerFileName);
			System.out.println("ftpServerFileName"+ftpServerFileName);
			loadingInfo.setFileSize(""+((remoteSize>1024)?
					(remoteSize/1024+"."+(remoteSize%1024)/1024*100+"kb"):
						remoteSize));
			loadingInfo.setHostName(ftpClient.getPassiveHost());
			System.out.println("ftpClient.getPassiveHost()="+ftpClient.getPassiveHost());
			
			loadingInfo.setLoadStatus("Start");
			loadingInfo.setLocalPath(localPath);
			System.out.println("localPath"+localPath);
			loadingInfo.setServerPath("/");
			loadingInfo.setLoadProgress(0);
			loadingInfo.setUpdatedTime(Calendar.getInstance().getTime());
			rowData = loadingInfoDao.addGetLoadingInfo(loadingInfo);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return rowData;
	}

	@Override
	public int updateProgress(int id,String ftpServerFileName, String localPath) throws Exception {
		
		System.out.println("in update progress info");
		System.out.println("localPath="+localPath);
		int progress = 0;
		try {
			FTPFile[] files = ftpClient.listFiles(ftpServerFileName);
			if (files.length != 1) {
				System.out.println("remote file is not unique!"+files.length);
				//return 0;
			}
			long serverSize = files[0].getSize();
			// start to calculate the progress
			File localFile = new File(localPath);
			System.out.println("localFile.exists()="+localFile.exists());
			if (localFile.exists()) {
				long localSize = localFile.length();
				
				System.out.println("localSize="+localSize);
				System.out.println("serverSize="+serverSize);
				progress = (int)((localSize/serverSize) * 100);
				System.out.println("progress-----------="+progress);
				LoadingInfo loadingInfo = loadingInfoDao.getLoadingInfo(id);
				System.out.println("id-----------="+id);
				loadingInfo.setLoadProgress(progress);
				loadingInfoDao.addLoadingInfo(loadingInfo);
				progress = loadingInfoDao.getLoadingInfo(id).getLoadProgress();
			} else {
				progress =0;
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		
		return progress;
	}

	private boolean downloadNewFile(File localFile, String remote)
			throws IOException {
		OutputStream out = new FileOutputStream(localFile);
		boolean result = ftpClient.retrieveFile(remote, out);
		out.close();
		return result;
	}

	private boolean retryBrokenDownloads(File localFile, String remote,
			long remoteSize) throws IOException {
		OutputStream out = new FileOutputStream(localFile, true);
		long localSize = localFile.length();
		System.out.println("Local file size is:" + localSize);
		if (localSize >= remoteSize) {
			System.out
					.println("Local file size is "
							+ "equal or bigger than remote file size, download stopped.");
			return false;
		}
		ftpClient.setRestartOffset(localSize);
		boolean result = ftpClient.retrieveFile(remote, out);
		out.close();
		return result;
	}

	public UploadStatus upload(String localPath, String ftpServerFileName)
			throws Exception {
		ftpClient.enterLocalPassiveMode();// transfer by passive mode
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// transfer date by byte
													// code
		UploadStatus result = null;

		// 对远程目录的处理
		String remoteFileName = ftpServerFileName;
		if (ftpServerFileName.contains("/")) {
			remoteFileName = ftpServerFileName.substring(ftpServerFileName
					.lastIndexOf("/") + 1);
			// 创建服务器远程目录结构，创建失败直接返回
			if (CreateDirecroty(ftpServerFileName, ftpClient) == UploadStatus.CREATE_DIRECTORY_FAIL) {
				return UploadStatus.CREATE_DIRECTORY_FAIL;
			}
		}

		// get remote file size
		FTPFile[] files = ftpClient.listFiles(ftpServerFileName);
		/*
		 * FTPFile[] files2 = ftpClient.listFiles("/"); for(FTPFile f : files2){
		 * //System.out.println(":"+f.getRawListing());
		 * System.out.println(":"+f.toFormattedString()); }
		 */
		// start to upload files
		File localFile = new File(localPath);
		if (files.length == 1) {
			long remoteSize = files[0].getSize();
			result = retryBrokenUploads(localFile, ftpServerFileName,
					remoteSize);
		} else {
			result = uploadNewFile(localFile, ftpServerFileName);
		}
		return result;
	}

	private UploadStatus CreateDirecroty(String remote, FTPClient ftpClient)
			throws IOException {
		UploadStatus status = UploadStatus.CREATE_DIRECTORY_SUCCESS;
		String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
		if (!directory.equalsIgnoreCase("/")
				&& !ftpClient.changeWorkingDirectory(new String(directory
						.getBytes("GBK"), "iso-8859-1"))) {
			// 如果远程目录不存在，则递归创建远程服务器目录
			int start = 0;
			int end = 0;
			if (directory.startsWith("/")) {
				start = 1;
			} else {
				start = 0;
			}
			end = directory.indexOf("/", start);
			while (true) {
				String subDirectory = new String(remote.substring(start, end)
						.getBytes("GBK"), "iso-8859-1");
				if (!ftpClient.changeWorkingDirectory(subDirectory)) {
					if (ftpClient.makeDirectory(subDirectory)) {
						ftpClient.changeWorkingDirectory(subDirectory);
					} else {
						System.out.println("创建目录失败");
						return UploadStatus.CREATE_DIRECTORY_FAIL;
					}
				}
				start = end + 1;
				end = directory.indexOf("/", start);

				// 检查所有目录是否创建完毕
				if (end <= start) {
					break;
				}
			}
		}
		return status;
	}

	private UploadStatus uploadNewFile(File localFile, String remote)
			throws IOException {
		InputStream in = new FileInputStream(localFile);
		UploadStatus result = null;
		if (ftpClient.storeFile(remote, in)) {
			result = UploadStatus.UPLOAD_NEW_FILES_SUCCESS;
		} else {
			result = UploadStatus.UPLOAD_NEW_FILES_FAILED;
		}
		in.close();
		return result;
	}

	private UploadStatus retryBrokenUploads(File localFile, String remote,
			long remoteSize) throws IOException {
		UploadStatus result = null;
		long localSize = localFile.length();
		System.out.println("Local file size is:" + localSize);
		if (remoteSize == localSize) {
			System.out.println("Remote file size is "
					+ "equal local file size, download stopped.");
			return UploadStatus.REMOTE_EQUAL_LOCAL;
		} else if (remoteSize > localSize) {
			System.out.println("Remote file size is "
					+ "bigger than local file size, download stopped.");
			return UploadStatus.REMOTE_BIGGER_LOCAL;
		}
		InputStream in = new FileInputStream(localFile);
		if (in.skip(remoteSize) == remoteSize) {
			ftpClient.setRestartOffset(remoteSize);
			if (ftpClient.storeFile(remote, in)) {
				result = UploadStatus.UPLOAD_FROM_BREAK_SUCCESS;
			} else {
				result = UploadStatus.UPLOAD_FROM_BREAK_FAILED;
			}
		}
		in.close();
		return result;
	}


	@Override
	public boolean uploadAll() {
		// TODO Auto-generated method stub
		return false;
	}

	

}
